/*******************************************************************************

  Pilot Intelligence Library
    https://gitee.com/pi-lab

*******************************************************************************/


#pragma once

#include <string>
#include <chrono>
#include <thread>
#include <mutex>

namespace pi {
    

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

class ScopedMutex
{
public:
    ScopedMutex(std::mutex& m_) : m(&m_) {
        m->lock();
    }

    ~ScopedMutex() {
        m->unlock();
    }

private:
    std::mutex* m;
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

class Thread
{
public:
    Thread() {
        m_isRunning = 0;
    }

    virtual ~Thread() {
        stop();
    }

    virtual void setName(const std::string& name) {
        m_threadName = name;
    }

    virtual void start(void) {
        m_isRunning = 1;
        m_thread = new std::thread(&Thread::threadFunc, this);
    }

    virtual void stop(void) {
        if( m_isRunning ) {
            m_isRunning = 0;
            m_thread->join();
            delete m_thread;
            m_thread = nullptr;
        }
    }

    virtual void threadFunc(void) = 0;

    virtual int isRunning(void) {
        return m_isRunning;
    }

    virtual int shouldStop(void) {
        return !m_isRunning;
    }

protected:
    std::thread     *m_thread;
    std::string     m_threadName;
    int             m_isRunning;
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

typedef void (*OSA_TIMER_FUNC)(void *data);

    
class TimerTask : public Thread
{
public:
    TimerTask() { 
        m_interval = 1000;
    }

    TimerTask(int interval, OSA_TIMER_FUNC func, void *arg) {
        m_interval = interval;

        start(func, arg);
    }
    
    virtual ~TimerTask() {
        stop();
    }

    virtual void setPeriodicInterval(int interval) {
        m_interval = interval;
    }
    
    virtual void start(OSA_TIMER_FUNC func, void *arg) {
        m_timerFunc = func;
        m_timerFuncArg = arg;
        
        stop(); // stop old thread if necessary

        Thread::start(); // start thread
    }
    
    virtual void threadFunc(void) {
        while( m_isRunning ) {
            auto t1 = std::chrono::high_resolution_clock::now();
            
            if( m_timerFunc ) m_timerFunc(m_timerFuncArg);
            
            auto t2 = std::chrono::high_resolution_clock::now();
            
            std::chrono::duration<double> diff = t2 - t1;
            double td = diff.count();
            if( td*1000 < m_interval ) {
                int nsleep = m_interval - td*1000;
                std::this_thread::sleep_for(std::chrono::milliseconds(nsleep));
            }
        }
    }

    
protected:
    int             m_interval;                 ///< milli second
    OSA_TIMER_FUNC  m_timerFunc;
    void*           m_timerFuncArg;
};
    

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

class RunProgram
{
public:
    RunProgram() { init(); }
    virtual ~RunProgram() { release(); }

    /**
     * @brief run a new program in new process
     *
     * @param cmd       - [in] command line
     *
     * @return 0        - Success
     */
    virtual int run(const std::string &cmd);

    /**
     * @brief stop the process
     */
    virtual void stop(void);

    int init(void);
    int release(void);

protected:
    std::string     m_cmd;
    void            *m_data;
};

} // end of namespace pi

